home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
050
/
madtrb30.arc
/
PIBASYNC.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1985-06-04
|
50KB
|
1,073 lines
(*----------------------------------------------------------------------*)
(* PIBASYNC.PAS --- Asynchronous I/O for Turbo Pascal *)
(*----------------------------------------------------------------------*)
(* *)
(* Author: Philip R. Burns *)
(* Date: January, 1985 *)
(* Version: 1.0 *)
(* Systems: For MS-DOS on IBM PCs and close compatibles only. *)
(* Note: I have checked these on Zenith 151s under *)
(* MSDOS 2.1 and IBM PCs under PCDOS 2.0. *)
(* *)
(* History: Some of these routines are based upon ones written by: *)
(* *)
(* Alan Bishop *)
(* C. J. Dunford *)
(* Michael Quinlan *)
(* *)
(* I have cleaned up these other authors' code, fixed some *)
(* bugs, and added many new features. *)
(* *)
(* Suggestions for improvements or corrections are welcome. *)
(* Please leave messages on Gene Plantz's BBS (312) 882 4145 *)
(* or Ron Fox's BBS (312) 940 6496. *)
(* *)
(* If you use this code in your own programs, please be nice *)
(* and give all of us credit. *)
(* *)
(*----------------------------------------------------------------------*)
(* *)
(* Routines: *)
(* *)
(* Async_Init --- Performs initialization. *)
(* Async_Open --- Sets up COM port *)
(* Async_Close --- Closes down COM port *)
(* Async_Carrier_Detect --- Checks for modem carrier detect *)
(* Async_Carrier_Drop --- Checks for modem carrier drop *)
(* Async_Buffer_Check --- Checks if character in COM buffer *)
(* Async_Term_Ready --- Toggles terminal ready status *)
(* Async_Receive --- Reads character from COM buffer *)
(* Async_Receive_With_Timeout *)
(* --- Receives char. with timeout check *)
(* Async_Send --- Transmits char over COM port *)
(* Async_Send_String --- Sends string over COM port *)
(* Async_Send_String_With_Delays *)
(* --- Sends string with timed delays *)
(* Async_Send_Break --- Sends break (attention) signal *)
(* Async_Percentage_Used --- Returns percentage com buffer used *)
(* Async_Purge_Buffer --- Purges receive buffer *)
(* *)
(*----------------------------------------------------------------------*)
(* *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* *)
(* COMMUNICATIONS HARDWARE ADDRESSES *)
(* *)
(* These are specific to IBM PCs and close compatibles. *)
(* *)
(*----------------------------------------------------------------------*)
Const
UART_THR = $00; (* offset from base of UART Registers for IBM PC *)
UART_RBR = $00;
UART_IER = $01;
UART_IIR = $02;
UART_LCR = $03;
UART_MCR = $04;
UART_LSR = $05;
UART_MSR = $06;
I8088_IMR = $21; (* port address of the Interrupt Mask Register *)
COM1_Base = $03F8; (* port addresses for the UART *)
COM2_Base = $02F8;
COM1_Irq = 4; (* Interrupt line for the UART *)
COM2_Irq = 3;
Const
Async_DSeg_Save : Integer = 0; (* Save DS reg in Code Segment for *)
(* interrupt routine *)
(*----------------------------------------------------------------------*)
(* *)
(* COMMUNICATIONS BUFFER VARIABLES *)
(* *)
(* The Communications Buffer is implemented as a circular (ring) *)
(* buffer, or double-ended queue. The asynchronous I/O routines *)
(* enter characters in the buffer as they are received. Higher- *)
(* level routines may extract characters from the buffer. *)
(* *)
(* Note that this buffer is used for input only; output is done *)
(* on a character-by-character basis. *)
(* *)
(*----------------------------------------------------------------------*)
Const
Async_Buffer_Max = 8191; (* Size of Communications Buffer *)
Async_Loops_Per_Sec = 6500; (* Loops per second -- 4.77 clock *)
TimeOut = 256; (* TimeOut value *)
Var
(* Communications Buffer Itself *)
Async_Buffer : Array[0..Async_Buffer_Max] of Char;
Async_Open_Flag : Boolean; (* true if Open but no Close *)
Async_Port : Integer; (* current Open port number (1 or 2) *)
Async_Base : Integer; (* base for current open port *)
Async_Irq : Integer; (* irq for current open port *)
Async_Buffer_Overflow : Boolean; (* True if buffer overflow has happened *)
Async_Buffer_Used : Integer;
Async_MaxBufferUsed : Integer;
(* Async_Buffer empty if Head = Tail *)
Async_Buffer_Head : Integer; (* Loc in Async_Buffer to put next char *)
Async_Buffer_Tail : Integer; (* Loc in Async_Buffer to get next char *)
Async_Buffer_NewTail : Integer;
(*----------------------------------------------------------------------*)
(* BIOS_RS232_Init --- Initialize UART *)
(*----------------------------------------------------------------------*)
Procedure BIOS_RS232_Init( ComPort, ComParm : Integer );
(* *)
(* Procedure: BIOS_RS232_Init *)
(* *)
(* Purpose: Issues interrupt $14 to initialize the UART *)
(* *)
(* Calling Sequence: *)
(* *)
(* BIOS_RS232_Init( ComPort, ComParm : Integer ); *)
(* *)
(* ComPort --- Communications Port Number (1 or 2) *)
(* ComParm --- Communications Parameter Word *)
(* *)
(* Calls: INTR (to perform BIOS interrupt $14) *)
(* *)
Var
Regs: RegPack;
Begin (* BIOS_RS232_Init *)
With Regs Do
Begin
Ax := ComParm AND $00FF; (* AH=0; AL=ComParm *)
Dx := ComPort; (* Port number to use *)
INTR($14, Regs);
End;
End (* BIOS_RS232_Init *);
(*----------------------------------------------------------------------*)
(* DOS_Set_Intrpt --- Call DOS to set interrupt vector *)
(*----------------------------------------------------------------------*)
Procedure DOS_Set_Intrpt( v, s, o : Integer );
(* *)
(* Procedure: DOS_Set_Intrpt *)
(* *)
(* Purpose: Calls DOS to set interrupt vector *)
(* *)
(* Calling Sequence: *)
(* *)
(* DOS_Set_Intrpt( v, s, o : Integer ); *)
(* *)
(* v --- interrupt vector number to set *)
(* s --- segment address of interrupt routine *)
(* o --- offset address of interrupt routine *)
(* *)
(* Calls: MSDOS (to set interrupt) *)
(* *)
Var
Regs : Regpack;
Begin (* DOS_Set_Intrpt *)
With Regs Do
Begin
Ax := $2500 + ( v AND $00FF );
Ds := s;
Dx := o;
MsDos( Regs );
End;
End (* DOS_Set_Intrpt *);
(*----------------------------------------------------------------------*)
(* Async_Isr --- Interrupt Service Routine *)
(*----------------------------------------------------------------------*)
Procedure Async_Isr;
(* *)
(* Procedure: Async_Isr *)
(* *)
(* Purpose: Invoked when UART has received character from *)
(* communications line (asynchronous) *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Isr; *)
(* *)
(* --- Called asyncronously only!!!!!! *)
(* *)
(* Remarks: *)
(* *)
(* This is Michael Quinlan's version of the interrupt handler. *)
(* *)
Begin (* Async_Isr *)
(* NOTE: on entry, Turbo Pascal has already PUSHed BP and SP *)
Inline(
(* save all registers used *)
$50/ (* PUSH AX *)
$53/ (* PUSH BX *)
$52/ (* PUSH DX *)
$1E/ (* PUSH DS *)
$FB/ (* STI *)
(* set up the DS register to point to Turbo Pascal's data segment *)
$2E/$FF/$36/Async_Dseg_Save/ (* PUSH CS:Async_Dseg_Save *)
$1F/ (* POP DS *)
(* get the incomming character *)
(* Async_Buffer[Async_Buffer_Head] := Chr(Port[UART_RBR + Async_Base]); *)
$8B/$16/Async_Base/ (* MOV DX,Async_Base *)
$EC/ (* IN AL,DX *)
$8B/$1E/Async_Buffer_Head/ (* MOV BX,Async_Buffer_Head *)
$88/$87/Async_Buffer/ (* MOV Async_Buffer[BX],AL *)
(* Async_Buffer_NewHead := Async_Buffer_Head + 1; *)
$43/ (* INC BX *)
(* if Async_Buffer_NewHead > Async_Buffer_Max then
Async_Buffer_NewHead := 0; *)
$81/$FB/Async_Buffer_Max/ (* CMP BX,Async_Buffer_Max *)
$7E/$02/ (* JLE L001 *)
$33/$DB/ (* XOR BX,BX *)
(* if Async_Buffer_NewHead = Async_Buffer_Tail then
Async_Buffer_Overflow := TRUE
else *)
(*L001:*)
$3B/$1E/Async_Buffer_Tail/ (* CMP BX,Async_Buffer_Tail *)
$75/$08/ (* JNE L002 *)
$C6/$06/Async_Buffer_Overflow/$01/ (* MOV Async_Buffer_Overflow,1 *)
$90/ (* NOP generated by assembler for some reason *)
$EB/$16/ (* JMP SHORT L003 *)
(* begin
Async_Buffer_Head := Async_Buffer_NewHead;
Async_Buffer_Used := Async_Buffer_Used + 1;
if Async_Buffer_Used > Async_MaxBufferUsed then
Async_MaxBufferUsed := Async_Buffer_Used
end; *)
(*L002:*)
$89/$1E/Async_Buffer_Head/ (* MOV Async_Buffer_Head,BX *)
$FF/$06/Async_Buffer_Used/ (* INC Async_Buffer_Used *)
$8B/$1E/Async_Buffer_Used/ (* MOV BX,Async_Buffer_Used *)
$3B/$1E/Async_MaxBufferUsed/ (* CMP BX,Async_MaxBufferUsed *)
$7E/$04/ (* JLE L003 *)
$89/$1E/Async_MaxBufferUsed/ (* MOV Async_MaxBufferUsed,BX *)
(*L003:*)
(* disable interrupts *)
$FA/ (* CLI *)
(* Port[$20] := $20; *) (* use non-specific EOI *)
$B0/$20/ (* MOV AL,20h *)
$E6/$20/ (* OUT 20h,AL *)
(* restore the registers then use IRET to return *)
(* the last two POPs are required because Turbo Pascal PUSHes these regs
before we get control. The manual doesn't say so, but that is what
really happens *)
$1F/ (* POP DS *)
$5A/ (* POP DX *)
$5B/ (* POP BX *)
$58/ (* POP AX *)
$5C/ (* POP SP *)
$5D/ (* POP BP *)
$CF) (* IRET *)
End (* Async_Isr *);
(*----------------------------------------------------------------------*)
(* Async_Init --- Initialize Asynchronous Variables *)
(*----------------------------------------------------------------------*)
Procedure Async_Init;
(* *)
(* Procedure: Async_Init *)
(* *)
(* Purpose: Initializes variables *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Init; *)
(* *)
(* Calls: None *)
(* *)
Begin (* Async_Init *)
Async_DSeg_Save := DSeg;
Async_Open_Flag := FALSE;
Async_Buffer_Overflow := FALSE;
Async_Buffer_Used := 0;
Async_MaxBufferUsed := 0;
End (* Async_Init *);
(*----------------------------------------------------------------------*)
(* Async_Close --- Close down communications interrupts *)
(*----------------------------------------------------------------------*)
Procedure Async_Close;
(* *)
(* Procedure: Async_Close *)
(* *)
(* Purpose: Resets interrupt system when UART interrupts *)
(* are no longer needed. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Close; *)
(* *)
(* Calls: None *)
(* *)
Var
i : Integer;
m : Integer;
Begin (* Async_Close *)
If Async_Open_Flag Then
Begin
(* disable the IRQ on the 8259 *)
Inline($FA); (* disable interrupts *)
i := Port[I8088_IMR]; (* get the interrupt mask register *)
m := 1 shl Async_Irq; (* set mask to turn off interrupt *)
Port[I8088_IMR] := i or m;
(* disable the 8250 data ready interrupt *)
Port[UART_IER + Async_Base] := 0;
(* disable OUT2 on the 8250 *)
Port[UART_MCR + Async_Base] := 0;
Inline($FB); (* enable interrupts *)
(* re-initialize our data areas so we know *)
(* the port is closed *)
Async_Open_Flag := FALSE;
End;
End (* Async_Close *);
(*----------------------------------------------------------------------*)
(* Async_Open --- Open communications port *)
(*----------------------------------------------------------------------*)
Function Async_Open( ComPort : Integer;
BaudRate : Integer;
Parity : Char;
WordSize : Integer;
StopBits : Integer ) : Boolean;
(* *)
(* Function: Async_Open *)
(* *)
(* Purpose: Opens communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Open( ComPort : Integer; *)
(* BaudRate : Integer; *)
(* Parity : Char; *)
(* WordSize : Integer; *)
(* StopBits : Integer) : Boolean; *)
(* *)
(* ComPort --- which port (1 or 2) *)
(* BaudRate --- Baud rate (110 to 9600) *)
(* Parity --- "E" for even, "O" for odd, "N" for none *)
(* WordSize --- Bits per character (5 through 8) *)
(* StopBits --- How many stop bits (1 or 2) *)
(* *)
(* Flag returned TRUE if port initialized successfully; *)
(* Flag returned FALSE if any errors. *)
(* *)
(* Calls: *)
(* *)
(* BIOS_RS232_Init --- initialize RS232 port *)
(* DOS_Set_Intrpt --- set address of RS232 interrupt routine *)
(* *)
Const (* Baud Rate Constants *)
Async_Num_Bauds = 8;
Async_Baud_Table : Array [1..Async_Num_Bauds] Of Record
Baud, Bits : Integer;
End
= ( ( Baud: 110; Bits: $00 ),
( Baud: 150; Bits: $20 ),
( Baud: 300; Bits: $40 ),
( Baud: 600; Bits: $60 ),
( Baud: 1200; Bits: $80 ),
( Baud: 2400; Bits: $A0 ),
( Baud: 4800; Bits: $C0 ),
( Baud: 9600; Bits: $E0 ) );
Var
ComParm : Integer;
i : Integer;
m : Integer;
Begin (* Async_Open *)
(* If port open, close it down first. *)
If Async_Open_Flag Then Async_Close;
(* Choose communications port *)
If ComPort = 2 Then
Begin
Async_Port := 2;
Async_Base := COM2_Base;
Async_Irq := COM2_Irq;
End
Else
Begin
Async_Port := 1; (* default to COM1 *)
Async_Base := COM1_Base;
Async_Irq := COM1_Irq;
End;
If (Port[UART_IIR + Async_Base] and $00F8) <> 0 Then
Async_Open := FALSE (* Serial port not installed *)
Else
Begin (* Open the port *)
(* Set buffer pointers *)
Async_Buffer_Head := 0;
Async_Buffer_Tail := 0;
Async_Buffer_Overflow := FALSE;
(*---------------------------------------------------*)
(* Build the ComParm for RS232_Init *)
(* See Technical Reference Manual for description *)
(*---------------------------------------------------*)
(* Set up the bits for the baud rate *)
If BaudRate > 9600 Then
BaudRate := 9600
Else If BaudRate <= 0 Then
BaudRate := 300;
i := 0;
Repeat
i := i + 1
Until ( ( i >= Async_Num_Bauds ) OR
( BaudRate = Async_Baud_Table[i].Baud ) );
ComParm := Async_Baud_Table[i].Bits;
(* Choose Parity *)
If Parity In ['E', 'e'] Then
ComParm := ComParm or $0018
Else If Parity In ['O', 'o'] Then
ComParm := ComParm or $0008;
(* Choose number of data bits *)
WordSize := WordSize - 5;
If ( WordSize < 0 ) OR ( WordSize > 3 ) Then
WordSize := 3;
ComParm := ComParm OR WordSize;
(* Choose stop bits *)
If StopBits = 2 Then
ComParm := ComParm OR $0004; (* default is 1 stop bit *)
(* use the BIOS COM port initialization routine *)
BIOS_RS232_Init( Async_Port - 1 , ComParm );
DOS_Set_Intrpt( Async_Irq + 8 , CSeg , Ofs( Async_Isr ) );
(* Read the RBR and reset any pending error conditions. *)
(* First turn off the Divisor Access Latch Bit to allow *)
(* access to RBR, etc. *)
Inline($FA); (* disable interrupts *)
Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] and $7F;
(* Read the Line Status Register to reset any errors *)
(* it indicates *)
i := Port[UART_LSR + Async_Base];
(* Read the Receiver Buffer Register in case it *)
(* contains a character *)
i := Port[UART_RBR + Async_Base];
(* enable the irq on the 8259 controller *)
i := Port[I8088_IMR]; (* get the interrupt mask register *)
m := (1 shl Async_Irq) xor $00FF;
Port[I8088_IMR] := i and m;
(* enable the data ready interrupt on the 8250 *)
Port[UART_IER + Async_Base] := $01;
(* enable OUT2 on 8250 *)
i := Port[UART_MCR + Async_Base];
Port[UART_MCR + Async_Base] := i or $08;
Inline($FB); (* enable interrupts *)
Async_Open := TRUE
End;
End (* Async_Open *);
(*----------------------------------------------------------------------*)
(* Async_Carrier_Detect --- Check for modem carrier detect *)
(*----------------------------------------------------------------------*)
Function Async_Carrier_Detect : Boolean;
(* *)
(* Function: Async_Carrier_Detect *)
(* *)
(* Purpose: Looks for modem carrier detect *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Carrier_Detect : Boolean; *)
(* *)
(* Flag is set TRUE if carrier detected, else FALSE. *)
(* *)
(* Calls: None *)
(* *)
Begin (* Async_Carrier_Detect *)
Async_Carrier_Detect := ODD( Port[ UART_MSR + Async_Base ] SHR 7 );
End (* Async_Carrier_Detect *);
(*----------------------------------------------------------------------*)
(* Async_Carrier_Drop --- Check for modem carrier drop/timeout *)
(*----------------------------------------------------------------------*)
Function Async_Carrier_Drop : Boolean;
(* *)
(* Function: Async_Carrier_Drop *)
(* *)
(* Purpose: Looks for modem carrier drop/timeout *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Carrier_Drop : Boolean; *)
(* *)
(* Flag is set TRUE if carrier dropped, else FALSE. *)
(* *)
(* Calls: None *)
(* *)
Begin (* Async_Carrier_Drop *)
Async_Carrier_Drop := NOT ODD( Port[ UART_MSR + Async_Base ] SHR 7 );
End (* Async_Carrier_Drop *);
(*----------------------------------------------------------------------*)
(* Async_Term_Ready --- Set terminal ready status *)
(*----------------------------------------------------------------------*)
Procedure Async_Term_Ready( Ready_Status : Boolean );
(* *)
(* Procedure: Async_Term_Ready *)
(* *)
(* Purpose: Sets terminal ready status *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Term_Ready( Ready_Status : Boolean ); *)
(* *)
(* Ready_Status --- Set TRUE to set terminal ready on, *)
(* Set FALSE to set terminal ready off. *)
(* *)
(* Calls: None *)
(* *)
Var
Mcr_Value: Byte;
Begin (* Async_Term_Ready *)
Mcr_Value := Port[ UART_MCR + Async_Base ];
If ODD( Mcr_Value ) Then Mcr_Value := Mcr_Value - 1;
If Ready_Status Then Mcr_Value := Mcr_Value + 1;
Port[ UART_MCR + Async_Base ] := Mcr_Value;
End (* Async_Term_Ready *);
(*----------------------------------------------------------------------*)
(* Async_Buffer_Check --- Check if character in buffer *)
(*----------------------------------------------------------------------*)
Function Async_Buffer_Check : Boolean;
(* *)
(* Function: Async_Buffer_Check *)
(* *)
(* Purpose: Check if character in buffer *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Buffer_Check : Boolean; *)
(* *)
(* Flag returned TRUE if character received in buffer, *)
(* Flag returned FALSE if no character received. *)
(* *)
(* Calls: None *)
(* *)
(* Remarks: *)
(* *)
(* This routine only checks if a character has been received *)
(* and thus can be read; it does NOT return the character. *)
(* Use Async_Receive to read the character. *)
(* *)
Begin (* Async_Buffer_Check *)
Async_Buffer_Check := ( Async_Buffer_Head <> Async_Buffer_Tail );
End (* Async_Buffer_Check *);
(*----------------------------------------------------------------------*)
(* Async_Receive --- Return character from buffer *)
(*----------------------------------------------------------------------*)
Function Async_Receive( Var C : Char ) : Boolean;
(* *)
(* Function: Async_Receive *)
(* *)
(* Purpose: Retrieve character (if any) from buffer *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Receive( Var C: Char ) : Boolean; *)
(* *)
(* C --- character (if any) retrieved from buffer; *)
(* set to CHR(0) if no character available. *)
(* *)
(* Flag returned TRUE if character retrieved from buffer, *)
(* Flag returned FALSE if no character retrieved. *)
(* *)
(* Calls: None *)
(* *)
Begin (* Async_Receive *)
If Async_Buffer_Head = Async_Buffer_Tail Then
Begin (* No character to retrieve *)
Async_Receive := FALSE;
C := CHR( 0 );
End (* No character available *)
Else
Begin (* Character available *)
(* Turn off interrupts *)
INLINE( $FA ); (* CLI --- Turn off interrupts *)
(* Get character from buffer *)
C := Async_Buffer[ Async_Buffer_Tail ];
(* Increment buffer pointer. If past *)
(* end of buffer, reset to beginning. *)
Async_Buffer_Tail := Async_Buffer_Tail + 1;
If Async_Buffer_Tail > Async_Buffer_Max Then
Async_Buffer_Tail := 0;
(* Decrement buffer use count *)
Async_Buffer_Used := Async_Buffer_Used - 1;
(* Turn on interrupts *)
INLINE( $FB ); (* STI --- Turn on interrupts *)
(* Indicate character successfully retrieved *)
Async_Receive := TRUE;
End (* Character available *);
End (* Async_Receive *);
(*----------------------------------------------------------------------*)
(* Async_Receive_With_TimeOut --- Return char. from buffer with delay *)
(*----------------------------------------------------------------------*)
Procedure Async_Receive_With_Timeout( Secs : Integer; Var C : Integer );
(* *)
(* Procedure: Async_Receive_With_Delay *)
(* *)
(* Purpose: Retrieve character as integer from buffer, *)
(* or return TimeOut if specified delay period *)
(* expires. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Receive_With_Timeout( Secs: Integer; Var C: Integer ); *)
(* *)
(* Secs --- Timeout period in seconds *)
(* C --- ORD(character) (if any) retrieved from buffer; *)
(* set to TimeOut if no character found before *)
(* delay period expires. *)
(* *)
(* Calls: Async_Receive *)
(* *)
(* WATCH OUT! THIS ROUTINE RETURNS AN INTEGER, NOT A CHARACTER!!! *)
(* *)
(* Note: This routine uses a CPU loop to do timing. The value of *)
(* the constant used is suitable for 4.77 MHz CPUs. If your *)
(* CPU is faster or slower, you will need to adjust the *)
(* value of ASYNC_LOOPS_PER_SEC. *)
(* *)
Var
Isecs : Integer;
Jsecs : Integer;
I : Integer;
J : Integer;
Char_Waiting : Boolean;
Ch : Char;
Begin (* Async_Receive_With_Timeout *)
I := Maxint DIV Async_Loops_Per_Sec;
Isecs := ( Secs + I - 1 ) DIV I;
Jsecs := ( Secs - Isecs * ( I - 1 ) ) * Async_Loops_Per_Sec;
Isecs := Isecs + 1;
Repeat
J := Jsecs;
Repeat
J := J - 1;
Char_Waiting := ( Async_Buffer_Head <> Async_Buffer_Tail );
Until( ( J = 0 ) OR ( Char_Waiting ) );
Isecs := Isecs - 1;
Until( ( Isecs = 0 ) OR ( Char_Waiting ) );
If ( NOT Char_Waiting) Then
C := TimeOut
Else
Begin
Char_Waiting := Async_Receive( Ch );
C := ORD( Ch );
End;
End (* Async_Receive_With_Timeout *);
(*----------------------------------------------------------------------*)
(* Async_Send --- Send character over communications port *)
(*----------------------------------------------------------------------*)
Procedure Async_Send( C : Char );
(* *)
(* Procedure: Async_Send *)
(* *)
(* Purpose: Sends character out over communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send( C : Char ); *)
(* *)
(* C --- Character to send *)
(* *)
(* Calls: None *)
(* *)
Var
i : Integer;
m : Integer;
Counter : Integer;
Begin (* Async_Send *)
(* Turn on OUT2, DTR, and RTS *)
Port[UART_MCR + Async_Base] := $0B;
(* Wait for CTS using Busy Wait *)
Counter := MaxInt;
While ( Counter <> 0 ) AND
( ( Port[UART_MSR + Async_Base] AND $10 ) = 0 ) Do
Counter := Counter - 1;
(* Wait for Transmit Hold Register Empty (THRE) *)
If Counter <> 0 Then Counter := MaxInt;
While ( Counter <> 0 ) AND
( ( Port[UART_LSR + Async_Base] AND $20 ) = 0 ) Do
Counter := Counter - 1;
(* Send the character if port clear *)
If Counter <> 0 Then
Begin (* Send the Character *)
Inline($FA); (* CLI --- disable interrupts *)
Port[UART_THR + Async_Base] := Ord(C);
Inline($FB); (* STI --- enable interrupts *)
End (* Send the Character *)
Else (* Timed Out *)
Writeln('<<<TIMEOUT>>>');
End (* Async_Send *);
(*----------------------------------------------------------------------*)
(* Async_Send_Break --- Send break (attention) signal *)
(*----------------------------------------------------------------------*)
Procedure Async_Send_Break;
(* *)
(* Procedure: Async_Send_Break *)
(* *)
(* Purpose: Sends break signal over communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_Break; *)
(* *)
(* Calls: None *)
(* *)
Var
Old_Lcr : Byte;
Break_Lcr : Byte;
Begin (* Async_Send_Break *)
Old_Lcr := Port[ UART_LCR + Async_Base ];
Break_Lcr := Old_Lcr;
If Break_Lcr > 127 Then Break_Lcr := Break_Lcr - 128;
If Break_Lcr <= 63 Then Break_Lcr := Break_Lcr + 64;
Port[ UART_LCR + Async_Base ] := Break_Lcr;
Delay( 400 );
Port[ UART_LCR + Async_Base ] := Old_Lcr;
End (* Async_Send_Break *);
(*----------------------------------------------------------------------*)
(* Async_Send_String --- Send string over communications port *)
(*----------------------------------------------------------------------*)
Procedure Async_Send_String( S : AnyStr );
(* *)
(* Procedure: Async_Send_String *)
(* *)
(* Purpose: Sends string out over communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_String( S : AnyStr ); *)
(* *)
(* S --- String to send *)
(* *)
(* Calls: Async_Send *)
(* *)
Var
i : Integer;
Begin (* Async_Send_String *)
For i := 1 To LENGTH( S ) Do
Async_Send( S[i] )
End (* Async_Send_String *);
(*----------------------------------------------------------------------*)
(* Async_Send_String_With_Delays --- Send string with timed delays *)
(*----------------------------------------------------------------------*)
Procedure Async_Send_String_With_Delays( S : AnyStr;
Char_Delay : Integer;
EOS_Delay : Integer );
(* *)
(* Procedure: Async_Send_String_With_Delays *)
(* *)
(* Purpose: Sends string out over communications port with *)
(* specified delays for each character and at the *)
(* end of the string. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_String_With_Delays( S : AnyStr ; *)
(* Char_Delay : Integer; *)
(* EOS_Delay : Integer ); *)
(* *)
(* S --- String to send *)
(* Char_Delay --- Number of milliseconds to delay after *)
(* sending each character *)
(* EOS_Delay --- Number of milleseconds to delay after *)
(* sending last character in string *)
(* *)
(* Calls: Async_Send *)
(* Async_Send_String *)
(* Length *)
(* Delay *)
(* *)
(* Remarks: *)
(* *)
(* This routine is useful when writing routines to perform *)
(* non-protocol uploads. Many computer systems require delays *)
(* between receipt of characters for correct processing. The *)
(* delay for end-of-string usually applies when the string *)
(* represents an entire line of a file. *)
(* *)
(* If delays are not required, Async_Send_String is faster. *)
(* This routine will call Async_Send_String is no character *)
(* delay is to be done. *)
(* *)
Var
I : Integer;
Begin (* Async_Send_String_With_Delays *)
If Char_Delay <= 0 Then
Async_Send_String( S )
Else
For I := 1 To LENGTH( S ) Do
Begin
Async_Send( S[I] );
Delay( Char_Delay );
End;
If EOS_Delay > 0 Then Delay( EOS_Delay );
End (* Async_Send_String_With_Delays *);
(*----------------------------------------------------------------------*)
(* Async_Percentage_Used --- Report Percentage Buffer Filled *)
(*----------------------------------------------------------------------*)
Function Async_Percentage_Used : Real;
(* *)
(* Function: Async_Percent_Used *)
(* *)
(* Purpose: Reports percentage of com buffer currently filled *)
(* *)
(* Calling Sequence: *)
(* *)
(* Percentage := Async_Percentage_Used : Real; *)
(* *)
(* Percentage gets how much of buffer is filled; *)
(* value goes from 0.0 (empty) to 1.0 (totally full). *)
(* *)
(* Calls: None *)
(* *)
(* Remarks: *)
(* *)
(* This routine is helpful when incorporating handshaking into *)
(* a communications program. For example, assume that the host *)
(* computer uses the XON/XOFF (DC1/DC3) protocol. Then the *)
(* PC program should issue an XOFF to the host when the value *)
(* returned by Async_Percentage_Used > .75 or so. When the *)
(* utilization percentage drops below .25 or so, the PC program *)
(* should transmit an XON. *)
(* *)
Begin (* Async_Percentage_Used *)
Async_Percentage_Used := Async_Buffer_Used / ( Async_Buffer_Max + 1 );
End (* Async_Percentage_Used *);
(*----------------------------------------------------------------------*)
(* Async_Purge_Buffer --- Purge communications input buffer *)
(*----------------------------------------------------------------------*)
Procedure Async_Purge_Buffer;
(* *)
(* Procedure: Async_Purge_Buffer *)
(* *)
(* Purpose: Purges communications input buffer *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Purge_Buffer; *)
(* *)
(* Calls: Async_Receive *)
(* *)
Var
C: Char;
Begin (* Async_Purge_Buffer *)
Repeat
Delay( 35 );
Until ( NOT Async_Receive( C ) );
End (* Async_Purge_Buffer *);